package com.wsllx.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Classname ApplicationConfigConfig
 * @Description 核心，自动装配对象
 * @Date 2022/9/22 21:11
 * @Created liulx
 */
public class AnnotationConfigApplicationContext {

    // 所传入的配置类
    private Class<?> configClass;

    // 存储bean的map集合
    private ConcurrentHashMap<String, BeanDefinition> beandefinitionMap = new ConcurrentHashMap();

    // 单例池（只建立一个对象的集合，单例池）
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    // 标识 beanpostprocessor的集合(前后处理集合？)
    private CopyOnWriteArrayList<BeanPostProcessor> beanpostProcesList = new CopyOnWriteArrayList<>();

    // 构造器,根据配置类构造环境
    public AnnotationConfigApplicationContext(Class<?> configClass) {
        this.configClass = configClass;

        // 扫描 判断类上是否有注解 @ComponentScan
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            // 获取 @ComponentScan 的 value 属性
            ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
            String path = componentScan.value();
            path = path.replace(".", "/");

            try {
                ClassLoader classLoader = AnnotationConfigApplicationContext.class.getClassLoader();
                URL resource = classLoader.getResource(path);
                File file = new File(resource.getFile());
                // 判断是否是文件夹
                if (file.isDirectory()) {
                    // 获取文件夹下所有的文件
                    File[] files = file.listFiles();
                    // 遍历文件
                    for (File f : files) {
                        // 获取绝对路径
                        String fileName = f.getAbsolutePath();
                        if (fileName.endsWith(".class")) {
                            path = path.replace("/", ".");
                            String newPath = path + "." + f.getName();
                            newPath = newPath.substring(0, newPath.length() - 6);
                            Class<?> clazz = classLoader.loadClass(newPath);

                            // 如果类中有 @Component 这个注解
                            if (clazz.isAnnotationPresent(Component.class)) {

                                // 判断当前类是否实现 BeanPostProcessor 接口  ？？？？
                                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                    // 创建一个实例
                                    BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.newInstance();
                                    // 添加到集合中
                                    beanpostProcesList.add(beanPostProcessor);
                                }
                                // 获取Bean的名字
                                String beanName = clazz.getAnnotation(Component.class).value();

                                //如果没有配置名字，默认是用首字母小写
                                if ("".equals(beanName)) {
                                    beanName = Introspector.decapitalize(clazz.getSimpleName());
                                }

                                BeanDefinition beanDefinition = new BeanDefinition();
                                // 设置bean的类型
                                beanDefinition.setType(clazz);
                                // 设置bean是否单例
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    Scope scope = clazz.getAnnotation(Scope.class);
                                    beanDefinition.setScope(scope.value());
                                } else {
                                    beanDefinition.setScope("singleton");
                                }
                                beandefinitionMap.put(beanName, beanDefinition);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 创建单例bean对象
        beandefinitionMap.keySet().forEach(beanName -> {
            BeanDefinition beanDefinition = beandefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = creteBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        });
    }

    /**
     * 创建对象,模拟bean的生命周期
     * 根据对象名称与作用域
     * @param beanName
     * @param beanDefinition
     * @return
     */
    private Object creteBean(String beanName, BeanDefinition beanDefinition) {
        Class type = beanDefinition.getType();
        try {
            Object instance = type.getConstructor().newInstance();

            // 获取类中所有属性，准确来说，spring原生的注入方式是先by type，再by name
            // 这边写的简单一点，直接用对应属性的名字作为beanName
            for (Field field : type.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    //??????
                    field.set(instance, getBean(field.getName()));
                }
            }

            // 回调 BeanNameAware
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }
            // 调用 BeanPostProcessor 的 postProcessBeforeInitialization
            for (BeanPostProcessor beanPostProcessor : beanpostProcesList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            // 初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }

            // 调用 BeanPostProcessor 的 postProcessAfterInitialization
            for (BeanPostProcessor beanPostProcessor : beanpostProcesList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }

            return instance;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据对象的名字获取对象
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {

        BeanDefinition beanDefinition = beandefinitionMap.get(beanName);
        if (beanDefinition == null) {
            return new RuntimeException("没有找到对应的对象");
        }
        //singleton单例,prototype多例
        //如果是单例则尝试从单例池子中获取一个对象，如果是多例则创建一个对象
        String scope = beanDefinition.getScope();
        if ("singleton".equals(scope)) {
            Object bean = singletonObjects.get(beanName);
            if (bean == null) {
                bean = creteBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
            return bean;
        } else {
            return creteBean(beanName, beanDefinition);
        }
    }
}
